package cn.maihe.elg.operation.centers.hymake.handler;

import cn.maihe.elg.operation.centers.hymake.auth.HyMakeCenterAuthConfig;
import cn.maihe.elg.operation.centers.hymake.auth.HyMakeHeaderInfo;
import cn.maihe.elg.operation.centers.hymake.dto.HyMakeBaseRespDTO;
import cn.maihe.elg.operation.centers.hymake.dto.handler.vo.xm.XMBaseRespDTO;
import cn.maihe.elg.operation.centers.hymake.enums.HyMakeRespCodeEnum;
import cn.maihe.elg.operation.centers.hymake.exception.HyMakeException;
import cn.maihe.elg.operation.centers.supports.annotation.CenterTradeService;
import cn.maihe.elg.operation.exception.CenterException;
import cn.maihe.elg.operation.exception.ExceptionUtils;
import cn.maihe.elg.operation.model.enums.CenterNoEnum;
import cn.maihe.elg.operation.model.enums.CenterProviderEnum;
import cn.maihe.elg.operation.model.enums.TradeType;
import cn.maihe.elg.operation.supports.system.ElgResourceService;
import cn.maihe.elg.operation.utils.Base64Util;
import cn.maihe.elg.operation.utils.DateUtil;
import cn.maihe.elg.operation.utils.ElgLogger;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.context.annotation.Profile;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.net.URLEncoder;
import java.nio.file.Path;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * @Description
 * @Author caixh
 * @Date 2022/3/30.
 */
@RestController
@RequestMapping(value = "/v1/center/" + XMCenterHandler.centerNo)
@Slf4j
public class XMCenterHandler {
    @Value("${spring.profiles:test}")
    private String profiles;

    public static final CenterProviderEnum centerProvider = CenterProviderEnum.XIANMEN;

    public static final String centerNo = "C0592";
    private final static CenterNoEnum centerNoEnum = CenterNoEnum.XIAMEN;
    /**
     * 中心服务商的接口实现类
     */
    private final Map<String, HyMakeCenterTradeService> tradeServiceMap = new HashMap<>();

    @javax.annotation.Resource
    private ElgResourceService elgResourceService;


    /**
     * 能够使用与该中心服务商的 中心信息
     */
    public static final List<String> enableCenterNos = Lists.newArrayList(CenterNoEnum.NANPING.getCenterNo());

    public XMCenterHandler(@SuppressWarnings("rawtypes") final Collection<HyMakeCenterTradeService> baseCenterServices) {
        super();
        baseCenterServices.forEach(item -> {
            CenterTradeService ts = AnnotationUtils.findAnnotation(item.getClass(), CenterTradeService.class);
            if (centerProvider == ts.provider()) {
                tradeServiceMap.put(ts.tradeType().name().toLowerCase(), item);
            }
        });
    }

    /**
     * 测试用
     */
    @GetMapping(value = "/xm/test/{orgCode}/revokeTemplate")
    public ResponseEntity<Resource> testGetRevokeTemplate(@PathVariable String orgCode) {
        Path path = elgResourceService.getFileByRelativePath("template/05退保申请书模版.doc").toPath();
        try {

            // 获取文件资源
            Resource resource = new UrlResource(path.toUri());

            // 设置响应头
            HttpHeaders headers = new HttpHeaders();
            // 替换空格
            String encodedFileName = URLEncoder.encode(path.getFileName().toString(), "UTF-8").replaceAll("\\+", "%20");
            headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName);

            // 返回ResponseEntity
            return ResponseEntity.ok().headers(headers).body(resource);
        } catch (IOException e) {
            throw HyMakeException.of(HyMakeRespCodeEnum.FAILURE, "获取退保模板失败");
        }
    }


    /**
     * 测试用Mock，用于生成加密数据、签名，然后调用正式的方法，验证解密、验签是否正确
     */
    @Profile(value = {"dev", "test"})
    @PostMapping(value = "/xm/test/{orgCode}/{tradeType}")
    @ResponseBody
    public Object testMockService(@PathVariable("orgCode") String orgCode, @PathVariable("tradeType") final String tradeType, @RequestHeader(required = false) Map<String, Object> headerMap,
//                                  @RequestBody final String clearBizReqJsonBody
                                  @RequestParam final Map<String, Object> reqBody
            , final HttpServletRequest req, final HttpServletResponse resp) {
        DeferredResult<Object> dr = new DeferredResult<Object>();

        CenterNoEnum centerNoEnum;
        try {
            centerNoEnum = CenterNoEnum.resolve(centerNo);
        } catch (Exception e) {
            log.error("暂不支持的交易中心[{}]:{}", centerNo, e.getMessage(), e);
            dr.setResult(buildRespException(HyMakeRespCodeEnum.FAILURE, "暂不支持的交易中心[" + centerNo + "]"));
            return dr;
        }

        HyMakeCenterTradeService hyMakeCenterTradeService = this.tradeServiceMap.get(tradeType.toLowerCase());
        HyMakeCenterAuthConfig hyMakeCenterAuthConfig = hyMakeCenterTradeService.getAuthConfig(CenterNoEnum.XIAMEN);


        //加密，只有lstApply才加密数据
        if (tradeType.equalsIgnoreCase(TradeType.lstApply.name())) {

            String params = (String) reqBody.get("TENDER_INFO");

            String encrypt = hyMakeCenterAuthConfig.getHyMakeEncryption().RSAEncrypt(profiles,params);

            // 更新解密数据
            reqBody.put("TENDER_INFO", encrypt);
        }


        Map<String, Object> ignore = new HashMap<>();
        ignore.put("SIGNATURE", null);
        ignore.put("fileKeys", null);
        ignore.put("file", null);

        // 设置签名
        String signature = hyMakeCenterAuthConfig.getHyMakeAuthorization().mockSignature(reqBody, ignore);
        reqBody.put("SIGNATURE", signature);
        return this.doService(centerNo, orgCode, tradeType, headerMap, req, resp, reqBody);
    }

    /**
     * 下载 05退保申请书模版.doc
     */
    @GetMapping(value = "/xm/{orgCode}/revokeTemplate")
    public ResponseEntity<Resource> getRevokeTemplate(@PathVariable String orgCode) {
        Path path = elgResourceService.getFileByRelativePath("template/05退保申请书模版.doc").toPath();
        try {

            // 获取文件资源
            Resource resource = new UrlResource(path.toUri());

            // 设置响应头
            HttpHeaders headers = new HttpHeaders();
            // 替换空格
            String encodedFileName = URLEncoder.encode(path.getFileName().toString(), "UTF-8").replaceAll("\\+", "%20");
            headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName);

            // 返回ResponseEntity
            return ResponseEntity.ok().headers(headers).body(resource);
        } catch (IOException e) {
            throw HyMakeException.of(HyMakeRespCodeEnum.FAILURE, "获取退保模板失败");
        }
    }

    /**
     *
     */
//    @PostMapping(value = "/xm/{orgCode}/{tradeType}", consumes = {"application/json", "application/xml"})
    @PostMapping(value = "/xm/{orgCode}/{tradeType}")
    @ResponseBody
    public DeferredResult<Object> service(@PathVariable("orgCode") String orgCode, @PathVariable("tradeType") final String tradeType, @RequestHeader(required = false) Map<String, Object> headerMap,
//                                          @RequestBody final String encryptReqBody,
                                          @RequestParam final Map<String, Object> reqBody, final HttpServletRequest req, final HttpServletResponse resp) {

//        return this.doService(centerNo, orgCode, tradeType, headerMap, req, resp, JSONObject.parseObject(encryptReqBody, Map.class));
        return this.doService(centerNo, orgCode, tradeType, headerMap, req, resp, reqBody);
    }

    @SuppressWarnings("unchecked")
    protected DeferredResult<Object> doService(String centerNo, String orgCode, String tradeType, Map<String, Object> headerMap, final HttpServletRequest req, final HttpServletResponse resp, Map<String, Object> reqBody) {

        DeferredResult<Object> dr = new DeferredResult<Object>();
        if (!this.tradeServiceMap.containsKey(tradeType.toLowerCase())) {
            dr.setResult(buildRespException(HyMakeRespCodeEnum.FAILURE, "不支持的交易类型"));
            return dr;
        }
        CenterNoEnum centerNoEnum;
        try {
            centerNoEnum = CenterNoEnum.resolve(centerNo);
        } catch (Exception e) {
            log.error("暂不支持的交易中心[{}]:{}", centerNo, e.getMessage(), e);
            dr.setResult(buildRespException(HyMakeRespCodeEnum.FAILURE, "暂不支持的交易中心[" + centerNo + "]"));
            return dr;
        }
//        log.info("请求头部参数:{}", JSON.toJSONString(headerMap));
        log.info("请求报文参数[明文加密字符串]:{}", JSON.toJSONString(reqBody));

//        HyMakeHeaderInfo headerInfo;
//        try {
//            headerInfo = HyMakeHeaderInfo.of(headerMap);
//        } catch (Exception e) {
//            log.error("请求头参数错误", e.getMessage(), e);
//            dr.setResult(buildRespException(HyMakeRespCodeEnum.FAILURE, "请求头参数错误[" + e.getMessage() + "]"));
//            return dr;
//        }

        HyMakeCenterTradeService hyMakeCenterTradeService = this.tradeServiceMap.get(tradeType.toLowerCase());
        HyMakeCenterAuthConfig hyMakeCenterAuthConfig = hyMakeCenterTradeService.getAuthConfig(centerNoEnum);

        HyMakeBaseRespDTO baseResp = HyMakeBaseRespDTO.error("初始化返回对象，默认错误");
        XMBaseRespDTO xmBaseRespDTO = null;
//        CompletableFuture.supplyAsync(() -> {
        try {

            Map<String, Object> ignore = new HashMap<>();
            ignore.put("SIGNATURE", null);

            // 获取文件流数据-单文件
            if (tradeType.equalsIgnoreCase(TradeType.lstClaim.name())) {

                MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) req;
                Iterator<String> fileNames = multipartRequest.getFileNames();
                String base64File = null;
                try {
                    while (fileNames.hasNext()) {
                        String keyName = fileNames.next();
                        MultipartFile file = multipartRequest.getFile(keyName);
                        base64File = Base64Util.encode2String(file.getBytes());
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }


                reqBody.put("file", base64File);
                ignore.put("file", null);
                // 获取文件流数据-多文件
            } else if (tradeType.equalsIgnoreCase(TradeType.filePush.name()) || tradeType.equalsIgnoreCase(TradeType.lstSurrend.name())) {

                MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) req;
                Iterator<String> fileNames = multipartRequest.getFileNames();
                Map<String, String> files = new HashMap<>();
//                    List<XMFilePushReqDTO.FileInfo> list = new ArrayList<>();

                try {
                    while (fileNames.hasNext()) {
                        String keyName = fileNames.next();
                        MultipartFile file = multipartRequest.getFile(keyName);
//                            byte[] bytes = file.getBytes();
                        // ... 处理文件 ...
//                            String newFileName = UUID.randomUUID() + "_" + file.getOriginalFilename();
//                            file.transferTo(new File("D:/" + newFileName));
//                            XMFilePushReqDTO.FileInfo fileInfo = new XMFilePushReqDTO.FileInfo();
//                            fileInfo.setFileType();
                        files.put(keyName, Base64Util.encode2String(file.getBytes()));
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }

                reqBody.put("fileKeys", files);


                ignore.put("fileKeys", null);
            }

            // 验签
            boolean verifySignature = this.verifySignature(hyMakeCenterAuthConfig, reqBody, (String) reqBody.get("SIGNATURE"), ignore);
            if (!verifySignature) {
                throw HyMakeException.of(HyMakeRespCodeEnum.FAILURE, "数据验签失败");
            }

            //解密，只有lstApply才加密数据
            if (tradeType.equalsIgnoreCase(TradeType.lstApply.name())) {

                String encrypt = (String) reqBody.get("TENDER_INFO");
                Map<String, Object> decrypt = this.decrypt(encrypt, hyMakeCenterAuthConfig);

                // 更新解密数据
                reqBody.put("TENDER_INFO", decrypt);
            }

            Map<String, Object> clearedParams = hyMakeCenterTradeService.reorganizeParams(reqBody);
            baseResp = hyMakeCenterTradeService.doService(centerNoEnum, orgCode, clearedParams, null);
        } catch (JSONException e) {
            throw HyMakeException.of(HyMakeRespCodeEnum.FAILURE, "请求数据转换为JSON格式失败");
        } catch (Exception e) {
            HyMakeException te = buildHyMakeException(e);
            log.error("中心：{},交易类型：{},服务错误：{}", centerNoEnum.getCenterName(), tradeType, e.getMessage(), e);
            xmBaseRespDTO = XMBaseRespDTO.builder()
                    .success(false)
                    .errorCode(te.getStatusEnum().getRespCode())
                    .errorDesc(te.getMessage())
                    .build();
//                throw buildHyMakeException(e);
        }
        //加密加签操作
        String encryptAllResp = JSONObject.toJSONString(xmBaseRespDTO == null ? baseResp : xmBaseRespDTO);
        dr.setResult(encryptAllResp);
        log.info("中心：{},交易类型：{},响应报文：{}", centerNoEnum.getCenterName(), tradeType, dr.getResult());
        return dr;
//        }).whenCompleteAsync((baseResp, t) -> {
//            XMBaseRespDTO xmBaseRespDTO = null;
//            if (t != null) {
//                HyMakeException te = buildHyMakeException(t);
//                log.error("中心：{},交易类型：{},服务错误：{}", centerNoEnum.getCenterName(), tradeType, t.getMessage(), t);
//                xmBaseRespDTO = XMBaseRespDTO.builder()
//                        .success(false)
//                        .errorCode(te.getStatusEnum().getRespCode())
//                        .errorDesc(te.getMessage())
//                        .build();
//            }
//
//            //加密加签操作
//            String encryptAllResp = JSONObject.toJSONString(xmBaseRespDTO == null ? baseResp : xmBaseRespDTO);
//
//            dr.setResult(encryptAllResp);
//            log.info("中心：{},交易类型：{},响应报文：{}", centerNoEnum.getCenterName(), tradeType, dr.getResult());
//        });
//        return dr;
    }


    private String encryptAndSignature(HyMakeHeaderInfo reqHeader, HttpServletResponse response, HyMakeBaseRespDTO baseResp, HyMakeCenterAuthConfig hyMakeCenterAuthConfig, CenterNoEnum centerNoEnum, String tradeType) {
        log.info("中心：{},交易类型：{},响应报文明文：{}", centerNoEnum.getCenterName(), tradeType, JSON.toJSONString(baseResp));
        Object data = baseResp.getData();
        //响应业务数据加密
        if (data != null) {
            if (centerNoEnum.equals(CenterNoEnum.QUANZHOU)) {
                String clearBizRespData = JSON.toJSONString(data);
                String encryptBizRespData = hyMakeCenterAuthConfig.getHyMakeEncryption().encrypt(clearBizRespData);
//            String encryptBizRespData = clearBizRespData;
                baseResp.setData(encryptBizRespData);
            }
        }

        //整个报文签名
        HyMakeHeaderInfo respHead = new HyMakeHeaderInfo();
        respHead.setVersion(hyMakeCenterAuthConfig.getCenterInfo().getVersion());
        respHead.setAppid(hyMakeCenterAuthConfig.getCenterInfo().getAppId());
//        respHead.setSign();
        respHead.setRequestid(reqHeader.getRequestid());
        respHead.setTimestamp(DateUtil.getFulltime());

        String allRespJson = JSON.toJSONString(baseResp);
        String signature;
        if (centerNoEnum.equals(CenterNoEnum.QUANZHOU)) {
            signature = hyMakeCenterAuthConfig.getHyMakeAuthorization().signatureSM3(respHead.getRequestid(), allRespJson, respHead.getTimestamp());
        } else {
            signature = hyMakeCenterAuthConfig.getHyMakeAuthorization().signature(respHead.getRequestid(), allRespJson, respHead.getTimestamp());
        }
        respHead.setSign(signature);

        BeanMap.create(respHead).forEach((k, v) -> response.setHeader((String) k, (String) v));

        String clearAllRespJson = JSON.toJSONString(baseResp);
        log.info("中心：{},交易类型：{},响应报文头：{},响应报文密文：{}", centerNoEnum.getCenterName(), tradeType, JSON.toJSONString(respHead), clearAllRespJson);

//        String encryptAllResp = hyMakeCenterAuthConfig.getHyMakeEncryption().encrypt(clearAllRespJson);

        return clearAllRespJson;
    }

    /**
     * 数据解密
     *
     * @param encrypt
     * @param hyMakeCenterAuthConfig
     * @return
     */
    private JSONObject decrypt(String encrypt, HyMakeCenterAuthConfig hyMakeCenterAuthConfig) {
        try {

            // 业务数据明文加密后字符串
            Assert.hasText(encrypt, "请求报文为空");

            String clearBizReqJsonBody = hyMakeCenterAuthConfig.getHyMakeEncryption().RSADecrypt(profiles,encrypt);

            ElgLogger.info(log, log -> log.info("解密完请求报文:{}", clearBizReqJsonBody));

            // 转换为JSON
            return JSON.parseObject(clearBizReqJsonBody);
        } catch (Exception e) {
            throw HyMakeException.of(HyMakeRespCodeEnum.FAILURE, "报文解密失败:" + e.getMessage(), e);
        }
    }

    public HyMakeException buildHyMakeException(CenterException ce) {
        HyMakeRespCodeEnum respCodeEnum = HyMakeRespCodeEnum.FAILURE;
        //待优化明细
        if (ce.getInnerRespCode().isFailure()) {
            respCodeEnum = HyMakeRespCodeEnum.FAILURE;
        } else if (ce.getInnerRespCode().isException()) {
            respCodeEnum = HyMakeRespCodeEnum.FAILURE;
        } else if (ce.getInnerRespCode().isTimeOut()) {
            respCodeEnum = HyMakeRespCodeEnum.FAILURE;
        }
        return HyMakeException.of(respCodeEnum, ce.getMessage());
    }


    protected boolean verifySignature(HyMakeCenterAuthConfig authConfig, Map<String, Object> reqBodyMap, String sinature, Map<String, Object> ignore) {
        try {
            return authConfig.getHyMakeAuthorization().verifySignature(reqBodyMap, sinature, ignore);

        } catch (Exception e) {
            throw HyMakeException.error("验签失败:" + e.getMessage(), e);
        }
    }

    private HyMakeBaseRespDTO buildRespException(HyMakeRespCodeEnum statusEnum, String msg) {
        return HyMakeBaseRespDTO.of(statusEnum, msg);
    }

    private HyMakeException buildHyMakeException(Throwable t) {
        HyMakeException te = ExceptionUtils.isAssignableFrom(HyMakeException.class, t);
        if (te == null) {
            CenterException ce = ExceptionUtils.isAssignableFrom(CenterException.class, t);
            if (ce != null) {
                te = buildHyMakeException(ce);
            } else {
                SocketTimeoutException timeout = ExceptionUtils.isAssignableFrom(SocketTimeoutException.class, t);
                if (timeout != null) {
                    te = HyMakeException.of(HyMakeRespCodeEnum.FAILURE, "请求超时", t);
                } else {
                    te = HyMakeException.error("请求错误:" + t.getMessage(), t);
                }
            }
        }
        return te;
    }

}
